from typing import Final

# PIME(Windows) key code
#  0x07 : unassigned
VK_BACK = 0x08
VK_TAB = 0x09
#  0x0A - = 0x0B : reserved
VK_CLEAR = 0x0C
VK_RETURN = 0x0D
VK_SHIFT = 0x10
VK_CONTROL = 0x11
VK_MENU = 0x12
VK_PAUSE = 0x13
VK_CAPITAL = 0x14
VK_KANA = 0x15
VK_HANGEUL = 0x15  # old name - should be here for compatibility
VK_HANGUL = 0x15
VK_JUNJA = 0x17
VK_FINAL = 0x18
VK_HANJA = 0x19
VK_KANJI = 0x19
VK_ESCAPE = 0x1B
VK_CONVERT = 0x1C
VK_NONCONVERT = 0x1D
VK_ACCEPT = 0x1E
VK_MODECHANGE = 0x1F
VK_SPACE = 0x20
VK_PRIOR = 0x21
VK_NEXT = 0x22
VK_END = 0x23
VK_HOME = 0x24
VK_LEFT = 0x25
VK_UP = 0x26
VK_RIGHT = 0x27
VK_DOWN = 0x28
VK_SELECT = 0x29
VK_PRINT = 0x2A
VK_EXECUTE = 0x2B
VK_SNAPSHOT = 0x2C
VK_INSERT = 0x2D
VK_DELETE = 0x2E
VK_HELP = 0x2F
#  VK_0 - VK_9 are the same as ASCII '0' - '9' (0x30 - = 0x39)
#  0x40 : unassigned
#  VK_A - VK_Z are the same as ASCII 'A' - 'Z' (0x41 - = 0x5A)
VK_LWIN = 0x5B
VK_RWIN = 0x5C
VK_APPS = 0x5D
#  0x5E : reserved
VK_SLEEP = 0x5F
VK_NUMPAD0 = 0x60
VK_NUMPAD1 = 0x61
VK_NUMPAD2 = 0x62
VK_NUMPAD3 = 0x63
VK_NUMPAD4 = 0x64
VK_NUMPAD5 = 0x65
VK_NUMPAD6 = 0x66
VK_NUMPAD7 = 0x67
VK_NUMPAD8 = 0x68
VK_NUMPAD9 = 0x69
VK_MULTIPLY = 0x6A
VK_ADD = 0x6B
VK_SEPARATOR = 0x6C
VK_SUBTRACT = 0x6D
VK_DECIMAL = 0x6E
VK_DIVIDE = 0x6F
VK_F1 = 0x70
VK_F2 = 0x71
VK_F3 = 0x72
VK_F4 = 0x73
VK_F5 = 0x74
VK_F6 = 0x75
VK_F7 = 0x76
VK_F8 = 0x77
VK_F9 = 0x78
VK_F10 = 0x79
VK_F11 = 0x7A
VK_F12 = 0x7B
#  0x88 - = 0x8F : unassigned
VK_NUMLOCK = 0x90
VK_SCROLL = 0x91
#  NEC PC-9800 kbd definitions
VK_OEM_NEC_EQUAL = 0x92  # '=' key on numpad
#  Fujitsu/OASYS kbd definitions
VK_OEM_FJ_JISHO = 0x92  # 'Dictionary' key
VK_OEM_FJ_MASSHOU = 0x93  # 'Unregister word' key
VK_OEM_FJ_TOUROKU = 0x94  # 'Register word' key
VK_OEM_FJ_LOYA = 0x95  # 'Left OYAYUBI' key
VK_OEM_FJ_ROYA = 0x96  # 'Right OYAYUBI' key
#  0x97 - = 0x9F : unassigned
#  VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys.
#  Used only as parameters to GetAsyncKeyState() and GetKeyState().
#  No other API or message will distinguish left and right keys in this way.
VK_LSHIFT = 0xA0
VK_RSHIFT = 0xA1
VK_LCONTROL = 0xA2
VK_RCONTROL = 0xA3
VK_LMENU = 0xA4
VK_RMENU = 0xA5
VK_BROWSER_BACK = 0xA6
VK_BROWSER_FORWARD = 0xA7
VK_BROWSER_REFRESH = 0xA8
VK_BROWSER_STOP = 0xA9
VK_BROWSER_SEARCH = 0xAA
VK_BROWSER_FAVORITES = 0xAB
VK_BROWSER_HOME = 0xAC
VK_VOLUME_MUTE = 0xAD
VK_VOLUME_DOWN = 0xAE
VK_VOLUME_UP = 0xAF
VK_MEDIA_NEXT_TRACK = 0xB0
VK_MEDIA_PREV_TRACK = 0xB1
VK_MEDIA_STOP = 0xB2
VK_MEDIA_PLAY_PAUSE = 0xB3
VK_LAUNCH_MAIL = 0xB4
VK_LAUNCH_MEDIA_SELECT = 0xB5
VK_LAUNCH_APP1 = 0xB6
VK_LAUNCH_APP2 = 0xB7
#  0xB8 - = 0xB9 : reserved
VK_OEM_1 = 0xBA  # ';:' for US
VK_OEM_PLUS = 0xBB  # '+' any country
VK_OEM_COMMA = 0xBC  # ',' any country
VK_OEM_MINUS = 0xBD  # '-' any country
VK_OEM_PERIOD = 0xBE  # '.' any country
VK_OEM_2 = 0xBF  # '/?' for US
VK_OEM_3 = 0xC0  # '`~' for US
#  0xC1 - = 0xD7 : reserved
#  0xD8 - = 0xDA : unassigned
VK_OEM_4 = 0xDB  # '[{' for US
VK_OEM_5 = 0xDC  # '\|' for US
VK_OEM_6 = 0xDD  # ']}' for US
VK_OEM_7 = 0xDE  # ''"' for US
VK_OEM_8 = 0xDF
#  0xE0 : reserved
#  Various extended or enhanced keyboards
VK_OEM_AX = 0xE1  # 'AX' key on Japanese AX kbd
VK_OEM_102 = 0xE2  # "<>" or "\|" on RT 102-key kbd.
VK_ICO_HELP = 0xE3  # Help key on ICO
VK_ICO_00 = 0xE4  # 00 key on ICO
VK_PROCESSKEY = 0xE5
VK_ICO_CLEAR = 0xE6
VK_PACKET = 0xE7
#  0xE8 : unassigned
#  Nokia/Ericsson definitions
VK_OEM_RESET = 0xE9
VK_OEM_JUMP = 0xEA
VK_OEM_PA1 = 0xEB
VK_OEM_PA2 = 0xEC
VK_OEM_PA3 = 0xED
VK_OEM_WSCTRL = 0xEE
VK_OEM_CUSEL = 0xEF
VK_OEM_ATTN = 0xF0
VK_OEM_FINISH = 0xF1
VK_OEM_COPY = 0xF2
VK_OEM_AUTO = 0xF3
VK_OEM_ENLW = 0xF4
VK_OEM_BACKTAB = 0xF5
VK_ATTN = 0xF6
VK_CRSEL = 0xF7
VK_EXSEL = 0xF8
VK_EREOF = 0xF9
VK_PLAY = 0xFA
VK_ZOOM = 0xFB
VK_NONAME = 0xFC
VK_PA1 = 0xFD
VK_OEM_CLEAR = 0xFE

# 按键名参考：
# https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent/code#code_values
VK_IM_ESC: Final[int] = 0x100  # non printable keys start with 256, skip 0 ~ 255
VK_IM_F1: Final[int] = 0x101
VK_IM_F2: Final[int] = 0x102
VK_IM_F3: Final[int] = 0x103
VK_IM_F4: Final[int] = 0x104
VK_IM_F5: Final[int] = 0x105
VK_IM_F6: Final[int] = 0x106
VK_IM_F7: Final[int] = 0x107
VK_IM_F8: Final[int] = 0x108
VK_IM_F9: Final[int] = 0x109
VK_IM_F10: Final[int] = 0x10a
VK_IM_F11: Final[int] = 0x10b
VK_IM_F12: Final[int] = 0x10c
VK_IM_BACKQUOTE: Final[int] = ord('`')  # `
VK_IM_1: Final[int] = ord('1')
VK_IM_2: Final[int] = ord('2')
VK_IM_3: Final[int] = ord('3')
VK_IM_4: Final[int] = ord('4')
VK_IM_5: Final[int] = ord('5')
VK_IM_6: Final[int] = ord('6')
VK_IM_7: Final[int] = ord('7')
VK_IM_8: Final[int] = ord('8')
VK_IM_9: Final[int] = ord('9')
VK_IM_0: Final[int] = ord('0')
VK_IM_MINUS: Final[int] = ord('-')
VK_IM_EQUAL: Final[int] = ord('=')
VK_IM_BACKSPACE: Final[int] = 0x10d

VK_IM_TAB: Final[int] = ord('\t')
VK_IM_Q: Final[int] = ord('Q')
VK_IM_W: Final[int] = ord('W')
VK_IM_E: Final[int] = ord('E')
VK_IM_R: Final[int] = ord('R')
VK_IM_T: Final[int] = ord('T')
VK_IM_Y: Final[int] = ord('Y')
VK_IM_U: Final[int] = ord('U')
VK_IM_I: Final[int] = ord('I')
VK_IM_O: Final[int] = ord('O')
VK_IM_P: Final[int] = ord('P')
VK_IM_BRACKET_LEFT: Final[int] = ord('[')
VK_IM_BRACKET_RIGHT: Final[int] = ord(']')
VK_IM_BACKSLASH: Final[int] = ord('\\')

VK_IM_CAPS_LOCK: Final[int] = 0x10e
VK_IM_A: Final[int] = ord('A')
VK_IM_S: Final[int] = ord('S')
VK_IM_D: Final[int] = ord('D')
VK_IM_F: Final[int] = ord('F')
VK_IM_G: Final[int] = ord('G')
VK_IM_H: Final[int] = ord('H')
VK_IM_J: Final[int] = ord('J')
VK_IM_K: Final[int] = ord('K')
VK_IM_L: Final[int] = ord('L')
VK_IM_SEMICOLON: Final[int] = ord(';')
VK_IM_QUOTE: Final[int] = ord("'")
VK_IM_ENTER: Final[int] = ord('\n')

VK_IM_SHIFT_LEFT: Final[int] = 0x10f
VK_IM_Z: Final[int] = ord('Z')
VK_IM_X: Final[int] = ord('X')
VK_IM_C: Final[int] = ord('C')
VK_IM_V: Final[int] = ord('V')
VK_IM_B: Final[int] = ord('B')
VK_IM_N: Final[int] = ord('N')
VK_IM_M: Final[int] = ord('M')
VK_IM_COMMA: Final[int] = ord(',')
VK_IM_PERIOD: Final[int] = ord('.')
VK_IM_SLASH: Final[int] = ord('/')
VK_IM_SHIFT_RIGHT: Final[int] = 0x110

VK_IM_CONTROL_LEFT: Final[int] = 0x111
VK_IM_ALT_LEFT: Final[int] = 0x112
VK_IM_SPACE: Final[int] = ord(' ')
VK_IM_ALT_RIGHT: Final[int] = 0x113
VK_IM_CONTROL_RIGHT: Final[int] = 0x114

VK_IM_PRINT_SCREEN: Final[int] = 0x200
VK_IM_SCROLL_LOCK: Final[int] = 0x201
VK_IM_PAUSE: Final[int] = 0x202
VK_IM_INSERT: Final[int] = 0x203
VK_IM_HOME: Final[int] = 0x204
VK_IM_PAGE_UP: Final[int] = 0x205
VK_IM_DELETE: Final[int] = 0x206
VK_IM_END: Final[int] = 0x207
VK_IM_PAGE_DOWN: Final[int] = 0x208

VK_IM_UP: Final[int] = 0x209
VK_IM_LEFT: Final[int] = 0x20a
VK_IM_DOWN: Final[int] = 0x20b
VK_IM_RIGHT: Final[int] = 0x20c

VK_IM_NUM_LOCK: Final[int] = 0x400
VK_IM_NUM_DIV: Final[int] = 0x401
VK_IM_NUM_MUL: Final[int] = 0x402
VK_IM_NUM_SUB: Final[int] = 0x403
VK_IM_NUM_ADD: Final[int] = 0x404
VK_IM_NUM_7: Final[int] = 0x417
VK_IM_NUM_8: Final[int] = 0x418
VK_IM_NUM_9: Final[int] = 0x419
VK_IM_NUM_4: Final[int] = 0x414
VK_IM_NUM_5: Final[int] = 0x415
VK_IM_NUM_6: Final[int] = 0x416
VK_IM_NUM_1: Final[int] = 0x411
VK_IM_NUM_2: Final[int] = 0x412
VK_IM_NUM_3: Final[int] = 0x413
VK_IM_NUM_0: Final[int] = 0x410
VK_IM_NUM_DEL: Final[int] = 0x405

_PIME_TO_IM = {
    VK_BACK: VK_IM_BACKSPACE,
    VK_TAB: VK_IM_TAB,
    VK_RETURN: VK_IM_ENTER,
    VK_SHIFT: VK_IM_SHIFT_LEFT,
    VK_CONTROL: VK_IM_CONTROL_LEFT,
    VK_MENU: VK_IM_ALT_LEFT,
    VK_PAUSE: VK_IM_PAUSE,
    VK_CAPITAL: VK_IM_CAPS_LOCK,
    VK_ESCAPE: VK_IM_ESC,
    VK_SPACE: VK_IM_SPACE,
    VK_PRIOR: VK_IM_PAGE_UP,
    VK_NEXT: VK_IM_PAGE_DOWN,
    VK_END: VK_IM_END,
    VK_HOME: VK_IM_HOME,
    VK_LEFT: VK_IM_LEFT,
    VK_UP: VK_IM_UP,
    VK_RIGHT: VK_IM_RIGHT,
    VK_DOWN: VK_IM_DOWN,
    VK_PRINT: VK_IM_PRINT_SCREEN,
    VK_INSERT: VK_IM_INSERT,
    VK_DELETE: VK_IM_DELETE,
    VK_NUMPAD0: VK_IM_NUM_0,
    VK_NUMPAD1: VK_IM_NUM_1,
    VK_NUMPAD2: VK_IM_NUM_2,
    VK_NUMPAD3: VK_IM_NUM_3,
    VK_NUMPAD4: VK_IM_NUM_4,
    VK_NUMPAD5: VK_IM_NUM_5,
    VK_NUMPAD6: VK_IM_NUM_6,
    VK_NUMPAD7: VK_IM_NUM_7,
    VK_NUMPAD8: VK_IM_NUM_8,
    VK_NUMPAD9: VK_IM_NUM_9,
    VK_MULTIPLY: VK_IM_NUM_MUL,
    VK_ADD: VK_IM_NUM_ADD,
    # VK_SEPARATOR:VK_IM_NUM_0,
    VK_SUBTRACT: VK_IM_NUM_SUB,
    VK_DECIMAL: VK_IM_NUM_DEL,
    VK_DIVIDE: VK_IM_NUM_DIV,
    VK_F1: VK_IM_F1,
    VK_F2: VK_IM_F2,
    VK_F3: VK_IM_F3,
    VK_F4: VK_IM_F4,
    VK_F5: VK_IM_F5,
    VK_F6: VK_IM_F6,
    VK_F7: VK_IM_F7,
    VK_F8: VK_IM_F8,
    VK_F9: VK_IM_F9,
    VK_F10: VK_IM_F10,
    VK_F11: VK_IM_F11,
    VK_F12: VK_IM_F12,
    VK_NUMLOCK: VK_IM_NUM_LOCK,
    VK_LSHIFT: VK_IM_SHIFT_LEFT,
    VK_RSHIFT: VK_IM_SHIFT_RIGHT,
    VK_LCONTROL: VK_IM_CONTROL_LEFT,
    VK_RCONTROL: VK_IM_CONTROL_RIGHT,
    VK_LMENU: VK_IM_ALT_LEFT,
    VK_RMENU: VK_IM_ALT_RIGHT,
    VK_OEM_PLUS: VK_IM_EQUAL,
    VK_OEM_COMMA: VK_IM_COMMA,
    VK_OEM_MINUS: VK_IM_MINUS,
    VK_OEM_PERIOD: VK_IM_PERIOD,
}

_NAME_TO_CODE = {
    'ESC': VK_IM_ESC,
    'F1': VK_IM_F1,
    'F2': VK_IM_F2,
    'F3': VK_IM_F3,
    'F4': VK_IM_F4,
    'F5': VK_IM_F5,
    'F6': VK_IM_F6,
    'F7': VK_IM_F7,
    'F8': VK_IM_F8,
    'F9': VK_IM_F9,
    'F10': VK_IM_F10,
    'F11': VK_IM_F11,
    'F12': VK_IM_F12,
    'BACKQUOTE': VK_IM_BACKQUOTE,
    '1': VK_IM_1,
    '2': VK_IM_2,
    '3': VK_IM_3,
    '4': VK_IM_4,
    '5': VK_IM_5,
    '6': VK_IM_6,
    '7': VK_IM_7,
    '8': VK_IM_8,
    '9': VK_IM_9,
    '0': VK_IM_0,
    'MINUS': VK_IM_MINUS,
    'EQUAL': VK_IM_EQUAL,
    'BACKSPACE': VK_IM_BACKSPACE,
    'TAB': VK_IM_TAB,
    'Q': VK_IM_Q,
    'W': VK_IM_W,
    'E': VK_IM_E,
    'R': VK_IM_R,
    'T': VK_IM_T,
    'Y': VK_IM_Y,
    'U': VK_IM_U,
    'I': VK_IM_I,
    'O': VK_IM_O,
    'P': VK_IM_P,
    'BRACKET_LEFT': VK_IM_BRACKET_LEFT,
    'BRACKET_RIGHT': VK_IM_BRACKET_RIGHT,
    'BACKSLASH': VK_IM_BACKSLASH,
    'CAPS_LOCK': VK_IM_CAPS_LOCK,
    'A': VK_IM_A,
    'S': VK_IM_S,
    'D': VK_IM_D,
    'F': VK_IM_F,
    'G': VK_IM_G,
    'H': VK_IM_H,
    'J': VK_IM_J,
    'K': VK_IM_K,
    'L': VK_IM_L,
    'SEMICOLON': VK_IM_SEMICOLON,
    'QUOTE': VK_IM_QUOTE,
    'ENTER': VK_IM_ENTER,

    'SHIFT': VK_IM_SHIFT_LEFT,
    'SHIFT_LEFT': VK_IM_SHIFT_LEFT,
    'Z': VK_IM_Z,
    'X': VK_IM_X,
    'C': VK_IM_C,
    'V': VK_IM_V,
    'B': VK_IM_B,
    'N': VK_IM_N,
    'M': VK_IM_M,
    'COMMA': VK_IM_COMMA,
    'PERIOD': VK_IM_PERIOD,
    'SLASH': VK_IM_SLASH,
    'SHIFT_RIGHT': VK_IM_SHIFT_RIGHT,

    'CTRL': VK_IM_CONTROL_LEFT,
    'CONTROL_LEFT': VK_IM_CONTROL_LEFT,
    'ALT': VK_IM_ALT_LEFT,
    'ALT_LEFT': VK_IM_ALT_LEFT,
    'SPACE': VK_IM_SPACE,
    'ALT_RIGHT': VK_IM_ALT_RIGHT,
    'CONTROL_RIGHT': VK_IM_RIGHT,

    'PRINT_SCREEN': VK_IM_PRINT_SCREEN,
    'SCROLL_LOCK': VK_IM_SCROLL_LOCK,
    'PAUSE': VK_IM_PAUSE,
    'INSERT': VK_IM_INSERT,
    'HOME': VK_IM_HOME,
    'PAGE_UP': VK_IM_PAGE_UP,
    'DELETE': VK_IM_DELETE,
    'END': VK_IM_END,
    'PAGE_DOWN': VK_IM_PAGE_DOWN,

    'UP': VK_IM_UP,
    'LEFT': VK_IM_LEFT,
    'DOWN': VK_IM_DOWN,
    'RIGHT': VK_IM_RIGHT,

    'NUM_LOCK': VK_IM_NUM_LOCK,
    'NUM_DIV': VK_IM_NUM_DIV,
    'NUM_MUL': VK_IM_NUM_MUL,
    'NUM_SUB': VK_IM_NUM_SUB,
    'NUM_ADD': VK_IM_NUM_ADD,
    'NUM_7': VK_IM_NUM_7,
    'NUM_8': VK_IM_NUM_8,
    'NUM_9': VK_IM_NUM_9,
    'NUM_4': VK_IM_NUM_4,
    'NUM_5': VK_IM_NUM_5,
    'NUM_6': VK_IM_NUM_6,
    'NUM_1': VK_IM_NUM_1,
    'NUM_2': VK_IM_NUM_2,
    'NUM_3': VK_IM_NUM_3,
    'NUM_0': VK_IM_NUM_0,
    'NUM_DEL': VK_IM_NUM_DEL,
}

_EDIT_KEYS_NAME = [
    'PrtScn',
    'ScrLk',
    'Pause',
    'Ins',
    'Home',
    'PgUp',
    'Del',
    'End',
    'PgDn',
    'Up',
    'Left',
    'Down',
    'Right',
]


class ImKeyEvent(object):
    kCtrlMask: Final[int] = 0b0000_1000  # 8
    kShiftMask: Final[int] = 0b0000_0100  # 4
    kAltMask: Final[int] = 0b0000_0010  # 2

    def __init__(self, pime_key: int = 0, pime_char: int = 0) -> None:
        super().__init__()
        self._modifier = 0
        self._code = 0
        if pime_key > 0 or pime_char > 0:
            self.apply(pime_key, pime_char)

    @property
    def modifier(self):
        return self._modifier

    @property
    def code(self):
        return self._code

    def isVisibleCharOnly(self) -> bool:
        return self.modifier == 0 and VK_IM_SPACE <= self._code < 128

    def getChar(self):
        if self.modifier == 0:
            if VK_IM_0 <= self.code <= VK_IM_9:
                return chr(self.code)
            if VK_IM_NUM_0 <= self.code == VK_IM_NUM_9:
                return chr(VK_IM_0 + self.code - VK_IM_NUM_0)
            if VK_IM_A <= self.code <= VK_IM_Z:
                return chr(self.code)
            if VK_IM_A <= self.code - 32 <= VK_IM_Z:
                return chr(self.code)
        return chr(self.code)

    @property
    def value(self):
        return (self._modifier << 16) | self._code

    def apply(self, pime_key: int = 0, pime_char: int = 0, seq: str = None):
        m = 0
        c = 0
        if seq is None or len(seq) == 0:
            if pime_key == VK_CONTROL:
                m = ImKeyEvent.kCtrlMask
            elif pime_key == VK_SHIFT:
                m = ImKeyEvent.kShiftMask
            elif pime_key == VK_MENU:
                m = ImKeyEvent.kAltMask
            else:
                if pime_key in _PIME_TO_IM:
                    c = _PIME_TO_IM.get(pime_key)
                elif pime_key == pime_char:
                    c = pime_key
                elif pime_char - pime_key == 32:  # lower
                    c = pime_char
                else:
                    c = pime_char
            m |= self._modifier
        else:
            for name in seq.split('+'):
                upper = name.upper()
                if upper == 'CTRL':
                    m |= ImKeyEvent.kCtrlMask
                elif upper == 'SHIFT':
                    m |= ImKeyEvent.kShiftMask
                elif upper == 'ALT':
                    m |= ImKeyEvent.kAltMask
                else:
                    c = _NAME_TO_CODE.get(upper)
        print('>>> <{}> apply() {}+{} -> {}+{}'.format(__name__, pime_key, pime_char, m, c))
        self._modifier = m
        self._code = c

    def updateModifiers(self, ctrl: bool, shift: bool, alt: bool):
        m = 0
        if ctrl:
            m |= ImKeyEvent.kCtrlMask
        else:
            m &= ~ImKeyEvent.kCtrlMask
        if shift:
            m |= ImKeyEvent.kShiftMask
        else:
            m &= ~ImKeyEvent.kShiftMask
        if alt:
            m |= ImKeyEvent.kAltMask
        else:
            m &= ~ImKeyEvent.kAltMask
        self._modifier = m

    def release(self, modifier: int = 0):
        m = self.modifier
        if modifier == VK_CONTROL:
            m &= ~ImKeyEvent.kCtrlMask
        elif modifier == VK_SHIFT:
            m &= ~ImKeyEvent.kShiftMask
        elif modifier == VK_MENU:
            m &= ~ImKeyEvent.kAltMask
        self._modifier = m

    def __str__(self) -> str:
        text = []
        if self.modifier > 0:
            if self.modifier & ImKeyEvent.kCtrlMask > 0:
                text.append('Ctrl')
            if self.modifier & ImKeyEvent.kShiftMask > 0:
                text.append('Sft')
            if self.modifier & ImKeyEvent.kAltMask > 0:
                text.append('Alt')
        if self.code == 0:
            pass
        elif self.code == VK_IM_ESC:  # invisible chars
            text.append('Esc')
        elif self.code == VK_IM_BACKSPACE:
            text.append('Bksp')
        elif self.code == VK_IM_TAB:
            text.append('Tab')
        elif self.code == VK_IM_ENTER:
            text.append('Ent')
        elif self.code == VK_IM_SPACE:
            text.append('Sp')
        elif VK_IM_SPACE < self.code < 128:  # visible chars
            text.append(chr(self.code))
        elif VK_IM_F1 <= self.code <= VK_IM_F12:  # function keys
            text.append('F' + str(self.code - VK_IM_F1 + 1))
        elif self.code & VK_IM_PRINT_SCREEN > 0:  # edit area
            text.append(_EDIT_KEYS_NAME[self.code - VK_IM_PRINT_SCREEN])
        elif self.code & VK_IM_NUM_LOCK > 0:  # num pad area
            if self.code == VK_IM_NUM_LOCK:
                text.append('NumLk')
            elif self.code == VK_IM_NUM_ADD:
                text.append('NumAdd')
            elif self.code == VK_IM_NUM_SUB:
                text.append('NumSub')
            elif self.code == VK_IM_NUM_MUL:
                text.append('NumMul')
            elif self.code == VK_IM_NUM_DIV:
                text.append('NumDiv')
            elif self.code == VK_IM_NUM_DEL:
                text.append('NumDel')
            elif VK_IM_NUM_0 <= self.code <= VK_IM_NUM_9:
                text.append('Num' + str(self.code - VK_IM_NUM_0))
        else:
            text.append(hex(self.code))
        return '+'.join(text)
